#ifndef FORTRESS_MAPS_H
#define FORTRESS_MAPS_H
#include "libtcod.hpp"
#include <vector>
#include <list>
#include <map>

#include "monster.h"
#include <boost/serialization/split_member.hpp>

namespace fortress
{
	struct newDoor
	{
		int x;
		int y;
		bool open;
		int side;
	};

	struct buildRoom
	{
		int x;
		int y;
		int width;
		int height;
		std::vector<newDoor> doors;
	};

	struct portal
	{
		friend class boost::serialization::access;
		template<class Archive>
		void serialize(Archive & ar, const unsigned int version);

		int x;
		int y;
		map * thisMap;
		portal * toPortal;
		int type;	// 0=downstairs, 1=upstairs, 2=teleport
	};

	struct tile
	{
		friend class boost::serialization::access;
		template<class Archive>
		void serialize(Archive & ar, const unsigned int version);
		char glyph;
		bool explored;
	};

	typedef std::vector<buildRoom> roomVec;
	class eventStructure;
	class eventReceiver;
	class items;
	class map;
	class mapController;
	class character;
	struct COORD;
	typedef std::map<int,std::list<items*>* > item_map_t;
	typedef std::pair<int,std::list<items*>* > item_map_pair_t;
	typedef std::vector <std::vector<tile> > mapData;

	class hallCallback : public ITCODPathCallback 
	{
	public: 
		virtual float getWalkCost( int xFrom, int yFrom, int xTo, int yTo, void *userData ) const;
	};

	class map
	{
	private:
		friend class boost::serialization::access;
		template<class Archive>
		void save(Archive & ar, const unsigned int version) const;
		template<class Archive>
		void load(Archive & ar, const unsigned int version);
		BOOST_SERIALIZATION_SPLIT_MEMBER()

		map();
		int width;				// Total x area
		int height;				// Total y area
		friend void fortress::monster::takeTurn(character * sameMap);
		mapController * gameMaps;
		std::list<character*> * monsterList; // Pointers to all the map's monsters
		std::list<character*> hearse;	// Pointers to all map monsters that have died this turn
		item_map_t * itemMap;	// Sorry. Use x*height + y to access the linked lists of items.
		std::map<int,items*> * itemData;	// A list of items which make up the main level (stairs, doors, etc.)
		TCODMap * levelMap;	// Visibility/pathfinding data for the map
		mapData * drawDataPtr;	// Draw data for the map
		//std::vector <map*> exits;	// Possible exits to this map
		std::vector <portal*> * portals; // Possible exits to this map
		void readItems();
		int convertXYList(int x, int y) const;
		void clearListIfEmpty(int num);
		void generateMap();
		bool intersects(buildRoom const & room1, buildRoom const & room2);
		void addPortal(int x, int y, map * setMap, portal * setPortal, int type);
		void buildDoorwaySetReset(int & x, int & y, int side, bool set);
	public:
		map(mapController *);
		map(portal * fromPortal, mapController * );
		~map();

		void calculateVisibility(int x, int y);
		// Checks to see if there is a character in a spot, and returns its event interface
		eventReceiver * getCharacter(int x, int y) const;
		// Checks to see if there is an item without a walkable flag
		bool getBlocked(int x, int y) const;
		// Gets the first upstair in the level, by checking every item until one is found
		void getUpstair(int &x, int &y);
		portal * getUpstairPortal();
		// Returns a the mapId of any portal matching the x, y, and type given
		// Returns -1 if no matching portal is found
		bool travelPortal(int & x, int & y, int type, map *& fromMap);
		// Places a character on the level
		void putCharacter(character * getChar); 
		// Removes a character from the level
		void releaseCharacter(character * dropChar); 
		// Deletes a character and removes it from the level
		void killCharacter(character * dropChar);
		std::list<COORD*> * getDrawList(bool redraw);
		void loadFile(char * filename);
		// Returns the value of a single tile
		char getTile(int x, int y) const;	
		// Has the player seen this tile?
		bool getExplored(int x, int y) const;
		// Fires an event for every item and character on a particular coordinate
		bool fireEventOnTile(eventStructure const & getEvent);
		// Places an item on the level
		// It assumes it is on a valid point in the map without checking
		void dropItem(items & item);
		// Gives a const list of all items at a particular point
		std::list<items*> const * getItemList(int x, int y) const;
		// Simply tells the user if there is an item list or not
		bool doesItemStackExist(int x, int y) const;
		// Removes an item from the map and returns it by reference
		items & pickupItem(int x, int y, items const * item);
		// Used to drop an inventory or item stack onto the level
		void mergeItemList(std::list<items*> & newItems);
		//
		void endTurn();

		void load();
	};
};
#endif